home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Source / HippoDraw / HippoDrawSrc1.1 / Hippo.subproj / Scribble.m < prev    next >
Encoding:
Text File  |  1992-04-25  |  6.8 KB  |  279 lines

  1. #import "Scribble.h"
  2. #import "GraphicView.h"
  3. #import "draw.h"
  4. #import <appkit/NXCursor.h>
  5. #import <appkit/nextstd.h>
  6. #import <dpsclient/dpsclient.h>
  7. #import <dpsclient/wraps.h>
  8.  
  9. @implementation Scribble : Graphic
  10.  
  11. static NXPoint lastPoint;    /* used in creating only */
  12.  
  13. + cursor
  14. /*
  15.  * A Scribble uses a pencil as its cursor.
  16.  */
  17. {
  18.     NXPoint spot;
  19.     static id cursor = nil;
  20.  
  21.     if (!cursor) {
  22.     cursor = [NXCursor newFromImage:[NXImage newFromSection:"pencil.tiff"]];
  23.     spot.x = 0.0; spot.y = 15.0;
  24.     [cursor setHotSpot:&spot];
  25.     }
  26.  
  27.     return cursor ? cursor : [super cursor];
  28. }
  29.  
  30. - free
  31. {
  32.     NX_FREE(points);
  33.     NX_FREE(userPathOps);
  34.     return [super free];
  35. }
  36.  
  37. - allocateChunk
  38. /*
  39.  * The Scribble's storage is allocated in chunks.
  40.  * This allocates another chunk.
  41.  */
  42. {
  43.     int i, newSize;
  44.  
  45.     newSize = length + CHUNK_SIZE;
  46.     if (points) {
  47.     NX_ZONEREALLOC([self zone], points, float, newSize << 1);
  48.     NX_ZONEREALLOC([self zone], userPathOps, char, newSize);
  49.     } else {
  50.     NX_ZONEMALLOC([self zone], points, float, newSize << 1);
  51.     NX_ZONEMALLOC([self zone], userPathOps, char, newSize);
  52.     }
  53.     for (i = newSize - 1; i >= length; i--) {
  54.     userPathOps[i] = dps_rlineto;
  55.     }
  56.  
  57.     return self;
  58. }
  59.  
  60. - (float)naturalAspectRatio
  61. /*
  62.  * The Scribble's natural aspect ratio is the one it was created with.
  63.  */
  64. {
  65.     return (gFlags.initialized ? ((bbox[2]-bbox[0])/(bbox[3]-bbox[1])) : 0.0);
  66. }
  67.  
  68. - (int)moveCorner:(int)corner to:(const NXPoint *)point constrain:(BOOL)flag
  69. /*
  70.  * After the Scribble is created (gFlags.initialized == YES), this method
  71.  * just returns super's implementation.  During creation, every time the
  72.  * "corner" is moved, a new line segment is added to the Scribble and
  73.  * the bounding box is expanded if necessary.
  74.  */
  75. {
  76.     float *p;
  77.  
  78.     if (gFlags.initialized) {
  79.     return [super moveCorner:corner to:point constrain:flag];
  80.     }
  81.  
  82.     if (!(point->x - lastPoint.x || point->y - lastPoint.y)) return corner;
  83.  
  84.     length++;
  85.  
  86.     if (!(length % CHUNK_SIZE)) [self allocateChunk];
  87.  
  88.     p = points + (length << 1);
  89.     *p++ = point->x - lastPoint.x;
  90.     *p = point->y - lastPoint.y;
  91.     lastPoint = *point;
  92.  
  93.     bbox[2] = MAX(point->x, bbox[2]);
  94.     bbox[0] = MIN(point->x, bbox[0]);
  95.     bbox[3] = MAX(point->y, bbox[3]);
  96.     bbox[1] = MIN(point->y, bbox[1]);
  97.  
  98.     bounds.origin.x = bbox[0];
  99.     bounds.origin.y = bbox[1];
  100.     bounds.size.width = bbox[2] - bbox[0];
  101.     bounds.size.height = bbox[3] - bbox[1];
  102.  
  103.     return corner;
  104. }
  105.  
  106.  
  107. - (BOOL)create:(NXEvent *)event in:view
  108. /*
  109.  * Before creating, an initial chunk is initialized, and the userPathOps
  110.  * are initialized.  The lastPoint is also remembered as the start point.
  111.  * After the Scribble is created, the initialized flag is set.
  112.  */
  113. {
  114.     NXPoint p;
  115.  
  116.     [self allocateChunk];
  117.     userPathOps[0] = dps_moveto;    
  118.     p = event->location;
  119.     [view convertPoint:&p fromView:nil];
  120.     [view grid:&p];
  121.     points[0] = p.x;
  122.     points[1] = p.y;
  123.     lastPoint = p;
  124.     bbox[0] = bbox[2] = p.x;
  125.     bbox[1] = bbox[3] = p.y;
  126.     bounds.origin = p;
  127.     bounds.size.width = bounds.size.height = 0.0;
  128.  
  129.     if ([super create:event in:view]) {
  130.     gFlags.initialized = YES;
  131.     return YES;
  132.     }
  133.  
  134.     return NO;
  135. }
  136.  
  137.  
  138. - draw
  139. /*
  140.  * The Scribble is drawn simply by scaling appropriately from its
  141.  * initial bounding box and drawing the user path.
  142.  */
  143. {
  144.     NXCoord x, y;
  145.     NXPoint p1, p2;
  146.     int i, count, coords;
  147.     float angle, sx, sy, tx, ty;
  148.  
  149.     if (bounds.size.width < 1.0 || bounds.size.height < 1.0) return self;
  150.  
  151.     if (length && (bbox[2] - bbox[0]) && (bbox[3] - bbox[1])) {
  152.     sx = bounds.size.width / (bbox[2] - bbox[0]);
  153.     sy = bounds.size.height / (bbox[3] - bbox[1]);
  154.     tx = (bounds.origin.x +
  155.           ((points[0]-bbox[0]) / (bbox[2]-bbox[0]) * bounds.size.width)) -
  156.         points[0] * sx;
  157.     ty = (bounds.origin.y +
  158.           ((points[1]-bbox[1]) / (bbox[3]-bbox[1]) * bounds.size.height)) -
  159.         points[1] * sy;
  160.     if (gFlags.arrow && NXEqualColor([self fillColor], NX_COLORCLEAR) &&
  161.         (sx != 1.0 || sy != 1.0 || tx || ty)) {
  162.         PSgsave();
  163.     }
  164.     if (!NXEqualColor([self fillColor], NX_COLORCLEAR)) {
  165.         if (!NXEqualColor([self lineColor], NX_COLORCLEAR)) PSgsave();
  166.         PStranslate(tx, ty);
  167.         PSscale(sx, sy);
  168.         [self setFillColor];
  169.         DPSDoUserPath(points, (length + 1) << 1, dps_float,
  170.               userPathOps, length + 1, bbox,
  171.               gFlags.eofill ? dps_ueofill : dps_ufill);
  172.         if (!NXEqualColor([self lineColor], NX_COLORCLEAR)) PSgrestore();
  173.     }
  174.     if (!NXEqualColor([self lineColor], NX_COLORCLEAR)) {
  175.         PStranslate(tx, ty);
  176.         PSscale(sx, sy);
  177.         [self setLineColor];
  178.         DPSDoUserPath(points, (length + 1) << 1, dps_float,
  179.               userPathOps, length + 1, bbox, dps_ustroke);
  180.     }
  181.     if (gFlags.arrow && NXEqualColor([self fillColor], NX_COLORCLEAR)) {
  182.         if (sx != 1.0 || sy != 1.0 || tx || ty) {
  183.         PSgrestore();
  184.         [self setLineColor];
  185.         }
  186.         if (gFlags.arrow != ARROW_AT_END) {
  187.         i = 0;
  188.         p1.x = points[i++];
  189.         p1.y = points[i++];
  190.         p2 = p1;
  191.         p2.x += points[i++];
  192.         p2.y += points[i++];
  193.         count = length - 1;
  194.         while (hypot((p1.x-p2.x)*sx,(p1.y-p2.y)*sy) < 7.0 && count--) {
  195.             p2.x += points[i++];
  196.             p2.y += points[i++];
  197.         }
  198.         angle = atan2((p1.y-p2.y)*sy, (p1.x-p2.x)*sx);
  199.         angle = (angle / 3.1415) * 180.0;
  200.         x = bounds.origin.x + (p1.x - bbox[0]) * sx;
  201.         y = bounds.origin.y + (p1.y - bbox[1]) * sy;
  202.         PSArrow(x, y, angle);
  203.         }
  204.         if (gFlags.arrow != ARROW_AT_START) {
  205.         i = 0;
  206.         coords = (length + 1) << 1;
  207.         p1.x = points[i++];
  208.         p1.y = points[i++];
  209.         while (i < coords) {
  210.             p1.x += points[i++];
  211.             p1.y += points[i++];
  212.         }
  213.         p2 = p1;
  214.         i = coords;
  215.         p2.y -= points[--i];
  216.         p2.x -= points[--i];
  217.         count = length - 1;
  218.         while (hypot((p2.x-p1.x)*sx,(p2.y-p1.y)*sy) < 7.0 && count--) {
  219.             p2.y -= points[--i];
  220.             p2.x -= points[--i];
  221.         }
  222.         angle = atan2((p1.y-p2.y)*sy, (p1.x-p2.x)*sx);
  223.         angle = (angle / 3.1415) * 180.0;
  224.         x = bounds.origin.x + (p1.x - bbox[0]) * sx;
  225.         y = bounds.origin.y + (p1.y - bbox[1]) * sy;
  226.         PSArrow(x, y, angle);
  227.         }
  228.     }
  229.     }
  230.  
  231.     return self;
  232. }
  233.  
  234. - write:(NXTypedStream *)stream
  235. /*
  236.  * The Scribble is written out by writing its length (in segments), its
  237.  * bounding box, and all the points.
  238.  */
  239. {
  240.     int i, numFloats;
  241.  
  242.     [super write:stream];
  243.  
  244.     NXWriteTypes(stream,"iffff",&length,&bbox[0],&bbox[1],&bbox[2],&bbox[3]);
  245.  
  246.     numFloats = (length + 1) << 1;
  247.     for (i = 0; i < numFloats; i++) {
  248.     NXWriteTypes(stream, "f", &points[i]);
  249.     }
  250.  
  251.     return self;
  252. }
  253.  
  254. - read:(NXTypedStream *)stream
  255. {
  256.     int i;
  257.     float *p;
  258.  
  259.     [super read:stream];
  260.  
  261.     NXReadTypes(stream,"iffff",&length,&bbox[0],&bbox[1],&bbox[2],&bbox[3]);
  262.  
  263.     NX_ZONEMALLOC([self zone], points, float, (length + 1) << 1);
  264.     NX_ZONEMALLOC([self zone], userPathOps, char, length + 1);
  265.  
  266.     p = points;
  267.     for (i = 0; i <= length; i++) {
  268.     NXReadTypes(stream, "f", p++);
  269.     NXReadTypes(stream, "f", p++);
  270.     userPathOps[i] = dps_rlineto;
  271.     }
  272.     userPathOps[0] = dps_moveto;
  273.  
  274.     return self;
  275. }
  276.  
  277. @end
  278.  
  279.